﻿// Decompiled with JetBrains decompiler
// Type: UnityEngine.Hash128
// Assembly: UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E6AE6B5A-2752-4B07-9528-C8B77C62CE79
// Assembly location: C:\Users\windys\Unity\2021.3.39f1c1\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll
// XML documentation location: C:\Users\windys\Unity\2021.3.39f1c1\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.xml

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Bindings;
using UnityEngine.Scripting;

#nullable disable
namespace UnityEngine
{
  /// <summary>
  ///   <para>Represents  a 128-bit hash value.</para>
  /// </summary>
  [UsedByNativeCode]
  [NativeHeader("Runtime/Utilities/Hash128.h")]
  [NativeHeader("Runtime/Export/Hashing/Hash128.bindings.h")]
  [Serializable]
  public struct Hash128 : IComparable, IComparable<Hash128>, IEquatable<Hash128>
  {
    internal ulong u64_0;
    internal ulong u64_1;
    private const ulong kConst = 16045690984833335023;

    /// <summary>
    ///   <para>Directly initialize a Hash128 with a 128-bit value.</para>
    /// </summary>
    /// <param name="u32_0">First 32 bits of hash value.</param>
    /// <param name="u32_1">Second 32 bits of hash value.</param>
    /// <param name="u32_2">Third 32 bits of hash value.</param>
    /// <param name="u32_3">Fourth 32 bits of hash value.</param>
    /// <param name="u64_0">First 64 bits of hash value.</param>
    /// <param name="u64_1">Second 64 bits of hash value.</param>
    public Hash128(uint u32_0, uint u32_1, uint u32_2, uint u32_3)
    {
      this.u64_0 = (ulong) u32_1 << 32 | (ulong) u32_0;
      this.u64_1 = (ulong) u32_3 << 32 | (ulong) u32_2;
    }

    /// <summary>
    ///   <para>Directly initialize a Hash128 with a 128-bit value.</para>
    /// </summary>
    /// <param name="u32_0">First 32 bits of hash value.</param>
    /// <param name="u32_1">Second 32 bits of hash value.</param>
    /// <param name="u32_2">Third 32 bits of hash value.</param>
    /// <param name="u32_3">Fourth 32 bits of hash value.</param>
    /// <param name="u64_0">First 64 bits of hash value.</param>
    /// <param name="u64_1">Second 64 bits of hash value.</param>
    public Hash128(ulong u64_0, ulong u64_1)
    {
      this.u64_0 = u64_0;
      this.u64_1 = u64_1;
    }

    /// <summary>
    ///   <para>Returns true is the hash value is valid. (Read Only)</para>
    /// </summary>
    public bool isValid => this.u64_0 != 0UL || this.u64_1 > 0UL;

    public int CompareTo(Hash128 rhs)
    {
      if (this < rhs)
        return -1;
      return this > rhs ? 1 : 0;
    }

    /// <summary>
    ///   <para>Convert a Hash128 to string.</para>
    /// </summary>
    public override string ToString() => Hash128.Hash128ToStringImpl(this);

    /// <summary>
    ///   <para>Convert a hex-encoded string into Hash128 value.</para>
    /// </summary>
    /// <param name="hashString">A hexadecimal-encoded hash string.</param>
    /// <returns>
    ///   <para>The 128-bit hash.</para>
    /// </returns>
    [FreeFunction("StringToHash128", IsThreadSafe = true)]
    public static Hash128 Parse(string hashString)
    {
      Hash128 ret;
      Hash128.Parse_Injected(hashString, out ret);
      return ret;
    }

    [FreeFunction("Hash128ToString", IsThreadSafe = true)]
    private static string Hash128ToStringImpl(Hash128 hash)
    {
      return Hash128.Hash128ToStringImpl_Injected(ref hash);
    }

    [FreeFunction("ComputeHash128FromScriptString", IsThreadSafe = true)]
    [MethodImpl(MethodImplOptions.InternalCall)]
    private static extern void ComputeFromString(string data, ref Hash128 hash);

    [FreeFunction("ComputeHash128FromScriptPointer", IsThreadSafe = true)]
    [MethodImpl(MethodImplOptions.InternalCall)]
    private static extern void ComputeFromPtr(
      IntPtr data,
      int start,
      int count,
      int elemSize,
      ref Hash128 hash);

    [FreeFunction("ComputeHash128FromScriptArray", IsThreadSafe = true)]
    [MethodImpl(MethodImplOptions.InternalCall)]
    private static extern void ComputeFromArray(
      Array data,
      int start,
      int count,
      int elemSize,
      ref Hash128 hash);

    /// <summary>
    ///   <para>Compute a hash of input data string.</para>
    /// </summary>
    /// <param name="data">Input data string. Note that Unity interprets the string as UTF-8 data, even if internally in C# strings are UTF-16.</param>
    /// <returns>
    ///   <para>The 128-bit hash.</para>
    /// </returns>
    public static Hash128 Compute(string data)
    {
      Hash128 hash = new Hash128();
      Hash128.ComputeFromString(data, ref hash);
      return hash;
    }

    public static unsafe Hash128 Compute<T>(NativeArray<T> data) where T : struct
    {
      Hash128 hash = new Hash128();
      Hash128.ComputeFromPtr((IntPtr) data.GetUnsafeReadOnlyPtr<T>(), 0, data.Length, UnsafeUtility.SizeOf<T>(), ref hash);
      return hash;
    }

    public static unsafe Hash128 Compute<T>(NativeArray<T> data, int start, int count) where T : struct
    {
      if (start < 0 || count < 0 || start + count > data.Length)
        throw new ArgumentOutOfRangeException(string.Format("Bad start/count arguments (start:{0} count:{1})", (object) start, (object) count));
      Hash128 hash = new Hash128();
      Hash128.ComputeFromPtr((IntPtr) data.GetUnsafeReadOnlyPtr<T>(), start, count, UnsafeUtility.SizeOf<T>(), ref hash);
      return hash;
    }

    public static Hash128 Compute<T>(T[] data) where T : struct
    {
      if (!UnsafeUtility.IsArrayBlittable((Array) data))
        throw new ArgumentException("Array passed to Compute must be blittable.\n" + UnsafeUtility.GetReasonForArrayNonBlittable((Array) data));
      Hash128 hash = new Hash128();
      Hash128.ComputeFromArray((Array) data, 0, data.Length, UnsafeUtility.SizeOf<T>(), ref hash);
      return hash;
    }

    public static Hash128 Compute<T>(T[] data, int start, int count) where T : struct
    {
      if (!UnsafeUtility.IsArrayBlittable((Array) data))
        throw new ArgumentException("Array passed to Compute must be blittable.\n" + UnsafeUtility.GetReasonForArrayNonBlittable((Array) data));
      if (start < 0 || count < 0 || start + count > data.Length)
        throw new ArgumentOutOfRangeException(string.Format("Bad start/count arguments (start:{0} count:{1})", (object) start, (object) count));
      Hash128 hash = new Hash128();
      Hash128.ComputeFromArray((Array) data, start, count, UnsafeUtility.SizeOf<T>(), ref hash);
      return hash;
    }

    public static Hash128 Compute<T>(List<T> data) where T : struct
    {
      if (!UnsafeUtility.IsGenericListBlittable<T>())
        throw new ArgumentException(string.Format("List<{0}> passed to {1} must be blittable.\n{2}", (object) typeof (T), (object) nameof (Compute), (object) UnsafeUtility.GetReasonForGenericListNonBlittable<T>()));
      Hash128 hash = new Hash128();
      Hash128.ComputeFromArray(NoAllocHelpers.ExtractArrayFromList((object) data), 0, data.Count, UnsafeUtility.SizeOf<T>(), ref hash);
      return hash;
    }

    public static Hash128 Compute<T>(List<T> data, int start, int count) where T : struct
    {
      if (!UnsafeUtility.IsGenericListBlittable<T>())
        throw new ArgumentException(string.Format("List<{0}> passed to {1} must be blittable.\n{2}", (object) typeof (T), (object) nameof (Compute), (object) UnsafeUtility.GetReasonForGenericListNonBlittable<T>()));
      if (start < 0 || count < 0 || start + count > data.Count)
        throw new ArgumentOutOfRangeException(string.Format("Bad start/count arguments (start:{0} count:{1})", (object) start, (object) count));
      Hash128 hash = new Hash128();
      Hash128.ComputeFromArray(NoAllocHelpers.ExtractArrayFromList((object) data), start, count, UnsafeUtility.SizeOf<T>(), ref hash);
      return hash;
    }

    public static unsafe Hash128 Compute<T>(ref T val) where T : unmanaged
    {
      fixed (T* data = &val)
      {
        Hash128 hash = new Hash128();
        Hash128.ComputeFromPtr((IntPtr) (void*) data, 0, 1, UnsafeUtility.SizeOf<T>(), ref hash);
        return hash;
      }
    }

    /// <summary>
    ///   <para>Compute a hash of input data.</para>
    /// </summary>
    /// <param name="val">Input value.</param>
    /// <returns>
    ///   <para>The 128-bit hash.</para>
    /// </returns>
    public static Hash128 Compute(int val)
    {
      Hash128 hash128 = new Hash128();
      hash128.Append(val);
      return hash128;
    }

    /// <summary>
    ///   <para>Compute a hash of input data.</para>
    /// </summary>
    /// <param name="val">Input value.</param>
    /// <returns>
    ///   <para>The 128-bit hash.</para>
    /// </returns>
    public static Hash128 Compute(float val)
    {
      Hash128 hash128 = new Hash128();
      hash128.Append(val);
      return hash128;
    }

    /// <summary>
    ///   <para>Compute a hash of input data.</para>
    /// </summary>
    /// <param name="data">Raw data pointer, usually used with C# stackalloc data.</param>
    /// <param name="size">Data size in bytes.</param>
    /// <returns>
    ///   <para>The 128-bit hash.</para>
    /// </returns>
    public static unsafe Hash128 Compute(void* data, ulong size)
    {
      Hash128 hash = new Hash128();
      Hash128.ComputeFromPtr(new IntPtr(data), 0, (int) size, 1, ref hash);
      return hash;
    }

    /// <summary>
    ///   <para>Hash new input string and combine with the current hash value.</para>
    /// </summary>
    /// <param name="data">Input data string. Note that Unity interprets the string as UTF-8 data, even if internally in C# strings are UTF-16.</param>
    public void Append(string data) => Hash128.ComputeFromString(data, ref this);

    public unsafe void Append<T>(NativeArray<T> data) where T : struct
    {
      Hash128.ComputeFromPtr((IntPtr) data.GetUnsafeReadOnlyPtr<T>(), 0, data.Length, UnsafeUtility.SizeOf<T>(), ref this);
    }

    public unsafe void Append<T>(NativeArray<T> data, int start, int count) where T : struct
    {
      if (start < 0 || count < 0 || start + count > data.Length)
        throw new ArgumentOutOfRangeException(string.Format("Bad start/count arguments (start:{0} count:{1})", (object) start, (object) count));
      Hash128.ComputeFromPtr((IntPtr) data.GetUnsafeReadOnlyPtr<T>(), start, count, UnsafeUtility.SizeOf<T>(), ref this);
    }

    public void Append<T>(T[] data) where T : struct
    {
      if (!UnsafeUtility.IsArrayBlittable((Array) data))
        throw new ArgumentException("Array passed to Append must be blittable.\n" + UnsafeUtility.GetReasonForArrayNonBlittable((Array) data));
      Hash128.ComputeFromArray((Array) data, 0, data.Length, UnsafeUtility.SizeOf<T>(), ref this);
    }

    public void Append<T>(T[] data, int start, int count) where T : struct
    {
      if (!UnsafeUtility.IsArrayBlittable((Array) data))
        throw new ArgumentException("Array passed to Append must be blittable.\n" + UnsafeUtility.GetReasonForArrayNonBlittable((Array) data));
      if (start < 0 || count < 0 || start + count > data.Length)
        throw new ArgumentOutOfRangeException(string.Format("Bad start/count arguments (start:{0} count:{1})", (object) start, (object) count));
      Hash128.ComputeFromArray((Array) data, start, count, UnsafeUtility.SizeOf<T>(), ref this);
    }

    public void Append<T>(List<T> data) where T : struct
    {
      if (!UnsafeUtility.IsGenericListBlittable<T>())
        throw new ArgumentException(string.Format("List<{0}> passed to {1} must be blittable.\n{2}", (object) typeof (T), (object) nameof (Append), (object) UnsafeUtility.GetReasonForGenericListNonBlittable<T>()));
      Hash128.ComputeFromArray(NoAllocHelpers.ExtractArrayFromList((object) data), 0, data.Count, UnsafeUtility.SizeOf<T>(), ref this);
    }

    public void Append<T>(List<T> data, int start, int count) where T : struct
    {
      if (!UnsafeUtility.IsGenericListBlittable<T>())
        throw new ArgumentException(string.Format("List<{0}> passed to {1} must be blittable.\n{2}", (object) typeof (T), (object) nameof (Append), (object) UnsafeUtility.GetReasonForGenericListNonBlittable<T>()));
      if (start < 0 || count < 0 || start + count > data.Count)
        throw new ArgumentOutOfRangeException(string.Format("Bad start/count arguments (start:{0} count:{1})", (object) start, (object) count));
      Hash128.ComputeFromArray(NoAllocHelpers.ExtractArrayFromList((object) data), start, count, UnsafeUtility.SizeOf<T>(), ref this);
    }

    public unsafe void Append<T>(ref T val) where T : unmanaged
    {
      fixed (T* data = &val)
        Hash128.ComputeFromPtr((IntPtr) (void*) data, 0, 1, UnsafeUtility.SizeOf<T>(), ref this);
    }

    /// <summary>
    ///   <para>Hash new input data and combine with the current hash value.</para>
    /// </summary>
    /// <param name="val">Input value.</param>
    public void Append(int val) => this.ShortHash4((uint) val);

    /// <summary>
    ///   <para>Hash new input data and combine with the current hash value.</para>
    /// </summary>
    /// <param name="val">Input value.</param>
    public unsafe void Append(float val) => this.ShortHash4(*(uint*) &val);

    /// <summary>
    ///   <para>Hash new input data and combine with the current hash value.</para>
    /// </summary>
    /// <param name="data">Raw data pointer, usually used with C# stackalloc data.</param>
    /// <param name="size">Data size in bytes.</param>
    public unsafe void Append(void* data, ulong size)
    {
      Hash128.ComputeFromPtr(new IntPtr(data), 0, (int) size, 1, ref this);
    }

    public override bool Equals(object obj) => obj is Hash128 hash128 && this == hash128;

    public bool Equals(Hash128 obj) => this == obj;

    public override int GetHashCode() => this.u64_0.GetHashCode() ^ this.u64_1.GetHashCode();

    public int CompareTo(object obj)
    {
      return obj == null || !(obj is Hash128) ? 1 : this.CompareTo((Hash128) obj);
    }

    public static bool operator ==(Hash128 hash1, Hash128 hash2)
    {
      return (long) hash1.u64_0 == (long) hash2.u64_0 && (long) hash1.u64_1 == (long) hash2.u64_1;
    }

    public static bool operator !=(Hash128 hash1, Hash128 hash2) => !(hash1 == hash2);

    public static bool operator <(Hash128 x, Hash128 y)
    {
      return (long) x.u64_0 != (long) y.u64_0 ? x.u64_0 < y.u64_0 : x.u64_1 < y.u64_1;
    }

    public static bool operator >(Hash128 x, Hash128 y) => !(x < y) && !(x == y);

    private void ShortHash4(uint data)
    {
      ulong u640 = this.u64_0;
      ulong u641 = this.u64_1;
      ulong num = 16045690984833335023;
      ulong h3 = (ulong) (-2401053088876216593L + 288230376151711744L);
      ulong h2 = num + (ulong) data;
      Hash128.ShortEnd(ref u640, ref u641, ref h2, ref h3);
      this.u64_0 = u640;
      this.u64_1 = u641;
    }

    private static void ShortEnd(ref ulong h0, ref ulong h1, ref ulong h2, ref ulong h3)
    {
      h3 ^= h2;
      Hash128.Rot64(ref h2, 15);
      h3 += h2;
      h0 ^= h3;
      Hash128.Rot64(ref h3, 52);
      h0 += h3;
      h1 ^= h0;
      Hash128.Rot64(ref h0, 26);
      h1 += h0;
      h2 ^= h1;
      Hash128.Rot64(ref h1, 51);
      h2 += h1;
      h3 ^= h2;
      Hash128.Rot64(ref h2, 28);
      h3 += h2;
      h0 ^= h3;
      Hash128.Rot64(ref h3, 9);
      h0 += h3;
      h1 ^= h0;
      Hash128.Rot64(ref h0, 47);
      h1 += h0;
      h2 ^= h1;
      Hash128.Rot64(ref h1, 54);
      h2 += h1;
      h3 ^= h2;
      Hash128.Rot64(ref h2, 32);
      h3 += h2;
      h0 ^= h3;
      Hash128.Rot64(ref h3, 25);
      h0 += h3;
      h1 ^= h0;
      Hash128.Rot64(ref h0, 63);
      h1 += h0;
    }

    private static void Rot64(ref ulong x, int k) => x = x << k | x >> 64 - k;

    [MethodImpl(MethodImplOptions.InternalCall)]
    private static extern void Parse_Injected(string hashString, out Hash128 ret);

    [MethodImpl(MethodImplOptions.InternalCall)]
    private static extern string Hash128ToStringImpl_Injected(ref Hash128 hash);
  }
}
